/*!
 * \file StrUtil.hpp
 * \project	WonderTrader
 *
 * \author Wesley
 * \date 2020/03/30
 * 
 * \brief 字符串操作工具类的封装
 * 
 * \details 提供丰富的字符串操作功能，包括：
 *          - 字符串修剪：去除空白字符、特定字符等
 *          - 字符串分割：支持多种分隔符和分割策略
 *          - 大小写转换：全大写、全小写转换
 *          - 字符串匹配：通配符匹配、前缀后缀判断
 *          - 格式化输出：类sprintf格式化功能
 *          - 路径处理：路径标准化、文件名分离等
 *          - 字符串查找替换：字符查找、字符串替换
 *          
 *          该工具类提供静态方法集合，无需实例化即可使用，
 *          为字符串处理提供高效便捷的接口。
 */
#pragma once
#include <string>
#include <vector>
#include <algorithm>
#include <sstream>
#include <functional>
#include <stdarg.h>
#include <stdint.h>
#include <string.h>
#include <cctype>

/*!
 * \brief 字符串向量类型定义
 * 
 * \details 用于存储字符串集合的向量类型，常用于字符串分割结果存储
 */
typedef std::vector<std::string> StringVector;

/*!
 * \brief 字符串工具类
 * 
 * \details 提供全面的字符串操作静态方法集合，包括：
 *          - 字符串修剪和清理
 *          - 字符串分割和连接
 *          - 大小写转换
 *          - 模式匹配和查找
 *          - 格式化和转换
 *          - 路径和文件名处理
 *          
 *          所有方法都是静态方法，可直接通过类名调用。
 *          提供高效的字符串处理能力，支持多种使用场景。
 */
class StrUtil
{
public:

	/*!
	 * \brief 移除字符串首尾的空白字符
	 * \param str 要处理的字符串（引用，会被修改）
	 * \param delims 要移除的字符集合，默认为空格、制表符、回车符
	 * \param left 是否移除左侧空白字符，默认为true
	 * \param right 是否移除右侧空白字符，默认为true
	 * 
	 * \details 移除字符串开头和/或结尾的指定字符。
	 *          用户可以指定只移除开头、结尾或两端的字符。
	 *          默认移除两端的空白字符。
	 */
	static inline void trim(std::string& str, const char* delims = " \t\r", bool left = true, bool right = true)
	{
		if(right)
			str.erase(str.find_last_not_of(delims)+1);
		if(left)
			str.erase(0, str.find_first_not_of(delims));
	}

	/*!
	 * \brief 移除字符串首尾的空白字符（不修改原字符串）
	 * \param str 要处理的字符串
	 * \param delims 要移除的字符集合，默认为空格、制表符、回车符
	 * \param left 是否移除左侧空白字符，默认为true
	 * \param right 是否移除右侧空白字符，默认为true
	 * \return 处理后的新字符串
	 * 
	 * \details 返回移除指定字符后的新字符串，不修改原始字符串
	 */
	static inline std::string trim(const char* str, const char* delims = " \t\r", bool left = true, bool right = true)
	{
		std::string ret = str;
		if(right)
			ret.erase(ret.find_last_not_of(delims)+1);
		if(left)
			ret.erase(0, ret.find_first_not_of(delims));

		return std::move(ret);
	}

	/*!
	 * \brief 去除字符串中的所有空格字符
	 * \param str 要处理的字符串（引用，会被修改）
	 * 
	 * \details 移除字符串中所有的空格字符，不仅仅是首尾的空格
	 */
	static inline void trimAllSpace(std::string &str)
	{
		std::string::iterator destEnd = std::remove_if(str.begin(), str.end(), [](const char& c){
			return c == ' ';
		});
		str.resize(destEnd-str.begin());
	}

	/*!
	 * \brief 去除字符串中的所有指定字符
	 * \param str 要处理的字符串（引用，会被修改）
	 * \param ch 要移除的字符
	 * 
	 * \details 移除字符串中所有指定的字符
	 */
	static inline void trimAll(std::string &str,char ch)
	{
		std::string::iterator destEnd=std::remove_if(str.begin(),str.end(),[ch](char c){ return c == ch; });
		str.resize(destEnd-str.begin());
	}

	/*!
	 * \brief 查找字符串中第一个指定字符的位置
	 * \param str 要搜索的字符串
	 * \param ch 要查找的字符
	 * \return 字符的位置索引，如果未找到返回std::string::npos
	 * 
	 * \details 在C风格字符串中查找指定字符第一次出现的位置
	 */
	static inline std::size_t findFirst(const char* str, char ch)
	{
		std::size_t i = 0;
		for(;;)
		{
			if (str[i] == ch)
				return i;

			if(str[i] == '\0')
				break;

			i++;
		}

		return std::string::npos;
	}

	/*!
	 * \brief 按分隔符分割字符串
	 * \param str 要分割的字符串
	 * \param delims 分隔符字符集合，默认为制表符、换行符、空格
	 * \param maxSplits 最大分割次数，0表示无限制，默认为0
	 * \return 包含所有子字符串的字符串向量
	 * 
	 * \details 返回包含所有由指定分隔符分割的子字符串的向量。
	 *          如果指定了最大分割次数，分割过程将在达到限制后停止。
	 */
	static inline StringVector split( const std::string& str, const std::string& delims = "\t\n ", unsigned int maxSplits = 0)
	{
		StringVector ret;
		unsigned int numSplits = 0;

		// Use STL methods
		size_t start, pos;
		start = 0;
		do
		{
			pos = str.find_first_of(delims, start);
			if (pos == start)
			{
				ret.emplace_back("");
				// Do nothing
				start = pos + 1;
			}
			else if (pos == std::string::npos || (maxSplits && numSplits == maxSplits))
			{
				// Copy the rest of the std::string
				ret.emplace_back( str.substr(start) );
				break;
			}
			else
			{
				// Copy up to delimiter
				ret.emplace_back( str.substr(start, pos - start) );
				start = pos + 1;
			}
			// parse up to next real data
			//start = str.find_first_not_of(delims, start);
			++numSplits;

		} while (pos != std::string::npos);
		return std::move(ret);
	}

	/*!
	 * \brief 按分隔符分割字符串（结果存储到指定容器）
	 * \param str 要分割的字符串
	 * \param ret 存储分割结果的字符串向量（引用）
	 * \param delims 分隔符字符集合，默认为制表符、换行符、空格
	 * \param maxSplits 最大分割次数，0表示无限制，默认为0
	 * 
	 * \details 将字符串分割结果直接存储到提供的向量中，避免创建新向量
	 */
	static inline void split(const std::string& str, StringVector& ret, const std::string& delims = "\t\n ", unsigned int maxSplits = 0)
	{
		unsigned int numSplits = 0;

		// Use STL methods
		size_t start, pos;
		start = 0;
		do
		{
			pos = str.find_first_of(delims, start);
			if (pos == start)
			{
				ret.emplace_back("");
				// Do nothing
				start = pos + 1;
			}
			else if (pos == std::string::npos || (maxSplits && numSplits == maxSplits))
			{
				// Copy the rest of the std::string
				ret.emplace_back(str.substr(start));
				break;
			}
			else
			{
				// Copy up to delimiter
				ret.emplace_back(str.substr(start, pos - start));
				start = pos + 1;
			}
			// parse up to next real data
			//start = str.find_first_not_of(delims, start);
			++numSplits;

		} while (pos != std::string::npos);
	}

	/*!
	 * \brief 将字符串转换为小写
	 * \param str 要转换的字符串（引用，会被修改）
	 * 
	 * \details 将字符串中的所有字符转换为小写形式
	 */
	static inline void toLowerCase( std::string& str )
	{
		std::transform(
			str.begin(),
			str.end(),
			str.begin(),
			(int(*)(int))tolower);

	}

	/*!
	 * \brief 将字符串转换为大写
	 * \param str 要转换的字符串（引用，会被修改）
	 * 
	 * \details 将字符串中的所有字符转换为大写形式
	 */
	static inline void toUpperCase( std::string& str )
	{
		std::transform(
			str.begin(),
			str.end(),
			str.begin(),
			(int(*)(int))toupper);
	}

	/*!
	 * \brief 创建字符串的小写副本
	 * \param str 要转换的字符串
	 * \return 转换为小写的新字符串
	 * 
	 * \details 返回原字符串的小写版本，不修改原字符串
	 */
	static inline std::string makeLowerCase(const char* str)
	{
		std::string strRet = str;
		std::transform(
			strRet.begin(),
			strRet.end(),
			strRet.begin(),
			(int(*)(int))tolower);
		return std::move(strRet);
	}

	/*!
	 * \brief 创建字符串的大写副本
	 * \param str 要转换的字符串
	 * \return 转换为大写的新字符串
	 * 
	 * \details 返回原字符串的大写版本，不修改原字符串
	 */
	static inline std::string makeUpperCase(const char* str)
	{
		std::string strRet = str;
		std::transform(
			strRet.begin(),
			strRet.end(),
			strRet.begin(),
			(int(*)(int))toupper);
		return std::move(strRet);
	}

	/*!
	 * \brief 将字符串转换为浮点数
	 * \param str 要转换的字符串
	 * \return 转换得到的浮点数值
	 * 
	 * \details 假设字符串只包含有效的可解析浮点数。
	 *          如果转换不可能，默认返回0.0
	 */
	static inline float toFloat( const std::string& str )
	{
		return (float)atof(str.c_str());
	}

	/*!
	 * \brief 将字符串转换为双精度浮点数
	 * \param str 要转换的字符串
	 * \return 转换得到的双精度浮点数值
	 * 
	 * \details 将字符串内容解析为双精度浮点数
	 */
	static inline double toDouble( const std::string& str )
	{
		return atof(str.c_str());
	}

	/*!
	 * \brief 检查字符串是否以指定模式开头
	 * \param str 要检查的字符串
	 * \param pattern 要匹配的模式字符串
	 * \param lowerCase 是否进行大小写不敏感比较，默认为true
	 * \return true表示字符串以模式开头，false表示不匹配
	 * 
	 * \details 如果lowerCase为true，在比较前会将字符串转换为小写，
	 *          此时pattern也应该是小写的
	 */
	static inline bool startsWith(const std::string& str, const std::string& pattern, bool lowerCase = true)
	{
		size_t thisLen = str.length();
		size_t patternLen = pattern.length();
		if (thisLen < patternLen || patternLen == 0)
			return false;

		std::string startOfThis = str.substr(0, patternLen);
		if (lowerCase)
			toLowerCase(startOfThis);

		return (startOfThis == pattern);
	}

	/*!
	 * \brief 检查字符串是否以指定模式结尾
	 * \param str 要检查的字符串
	 * \param pattern 要匹配的模式字符串
	 * \param lowerCase 是否进行大小写不敏感比较，默认为true
	 * \return true表示字符串以模式结尾，false表示不匹配
	 * 
	 * \details 如果lowerCase为true，在比较前会将字符串转换为小写，
	 *          此时pattern也应该是小写的
	 */
	static inline bool endsWith(const std::string& str, const std::string& pattern, bool lowerCase = true)
	{
		size_t thisLen = str.length();
		size_t patternLen = pattern.length();
		if (thisLen < patternLen || patternLen == 0)
			return false;

		std::string endOfThis = str.substr(thisLen - patternLen, patternLen);
		if (lowerCase)
			toLowerCase(endOfThis);

		return (endOfThis == pattern);
	}

	/*!
	 * \brief 标准化路径格式
	 * \param init 初始路径字符串
	 * \param bIsDir 是否为目录路径，默认为true
	 * \return 标准化后的路径字符串
	 * 
	 * \details 标准化路径的方法：使用正斜杠分隔符，目录路径以斜杠结尾
	 */
	static inline std::string standardisePath( const std::string &init, bool bIsDir = true)
	{
		std::string path = init;

		std::replace( path.begin(), path.end(), '\\', '/' );
		if (path[path.length() - 1] != '/' && bIsDir)
			path += '/';

		return std::move(path);
	}

	/*!
	 * \brief 分离完整文件名的基础名称和路径
	 * \param qualifiedName 完整的文件路径名
	 * \param outBasename 输出参数，存储基础文件名
	 * \param outPath 输出参数，存储路径部分
	 * 
	 * \details 将完整的文件路径分离为基础文件名和路径两部分。
	 *          路径按standardisePath标准化
	 */
	static inline void splitFilename(const std::string& qualifiedName,std::string& outBasename, std::string& outPath)
	{
		std::string path = qualifiedName;
		// Replace \ with / first
		std::replace( path.begin(), path.end(), '\\', '/' );
		// split based on final /
		size_t i = path.find_last_of('/');

		if (i == std::string::npos)
		{
			outPath.clear();
			outBasename = qualifiedName;
		}
		else
		{
			outBasename = path.substr(i+1, path.size() - i - 1);
			outPath = path.substr(0, i+1);
		}
	}

	/*!
	 * \brief 通配符模式匹配
	 * \param str 要匹配的字符串
	 * \param pattern 匹配模式（支持*和?通配符）
	 * \param caseSensitive 是否大小写敏感，默认为true
	 * \return true表示匹配成功，false表示匹配失败
	 * 
	 * \details 支持通配符模式匹配：
	 *          - * 匹配任意数量的字符
	 *          - ? 匹配单个字符
	 */
	static inline bool match(const std::string& str, const std::string& pattern, bool caseSensitive = true)
	{
		std::string tmpStr = str;
		std::string tmpPattern = pattern;
		if (!caseSensitive)
		{
			toLowerCase(tmpStr);
			toLowerCase(tmpPattern);
		}

		std::string::const_iterator strIt = tmpStr.begin();
		std::string::const_iterator patIt = tmpPattern.begin();
		std::string::const_iterator lastWildCardIt = tmpPattern.end();
		std::string::const_iterator strItBackup;

		while (strIt != tmpStr.end() && patIt != tmpPattern.end())
		{
			if (*patIt == '*')
			{
				lastWildCardIt = patIt;
				strItBackup = strIt;
				++patIt;
			}
			else if (*patIt == '?' || *strIt == *patIt)
			{
				++patIt;
				++strIt;
			}
			else
			{
				if (lastWildCardIt != tmpPattern.end())
				{
					patIt = lastWildCardIt;
					strIt = ++strItBackup;
				}
				else
				{
					return false;
				}
			}
		}
		// skip over any leftover wildcards
		while ( patIt != tmpPattern.end() && *patIt == '*' )
		{
			++patIt;
		}

		return patIt == tmpPattern.end() && strIt == tmpStr.end();
	}

	/*!
	 * \brief 返回空白字符串常量
	 * \return 空白字符串的常量引用
	 * 
	 * \details 提供一个全局的空白字符串常量
	 */
	static inline const std::string BLANK()
	{
		return "";
	}

	/*!
	 * \brief 格式化字符串（可变参数版本）
	 * \param pszFormat 格式化字符串
	 * \param ... 可变参数
	 * \return 格式化后的字符串
	 * 
	 * \details 类似sprintf的格式化功能，返回格式化后的字符串
	 */
	static inline std::string printf(const char *pszFormat, ...)
	{
		va_list ap;
		va_start(ap, pszFormat);
		std::string retStr = printf(pszFormat, ap);
		va_end(ap);
		return retStr;
	}

	/*!
	 * \brief 格式化字符串（可变参数版本2）
	 * \param pszFormat 格式化字符串
	 * \param ... 可变参数
	 * \return 格式化后的字符串
	 * 
	 * \details 另一个版本的printf实现
	 */
	static inline std::string printf2(const char *pszFormat, ...)
	{
		va_list ap;
		va_start(ap, pszFormat);
		std::string retStr = printf2(pszFormat, ap);
		va_end(ap);
		return retStr;
	}

	/*!
	 * \brief 格式化字符串（va_list版本2）
	 * \param pszFormat 格式化字符串
	 * \param argptr 参数列表
	 * \return 格式化后的字符串
	 * 
	 * \details 使用va_list的printf实现，支持嵌套调用
	 */
	static inline std::string printf2(const char *pszFormat,va_list argptr)
	{
		char* buffer = NULL;
		int bufflen = 256;
		
		while(true)
		{
			buffer = new char[bufflen];
			va_list ap;
			va_copy(ap, argptr);
			int writelen = vsnprintf(buffer, bufflen, pszFormat, ap);
			va_end(ap);
			if(writelen < bufflen && writelen != -1)
			{
				std::string s(buffer);
				delete[] buffer;
				return s;
			}
			delete[] buffer;
			if(writelen == -1)
				bufflen*=2;
			else
				bufflen = writelen + 1;
		}
		return "";
	}

	/*!
	 * \brief 字符串扩展到指定长度
	 * \param str 原始字符串
	 * \param length 目标长度
	 * \return 扩展后的字符串
	 * 
	 * \details 如果字符串长度小于指定长度，用空格填充到指定长度
	 */
	static inline std::string extend(const char* str, uint32_t length)
	{
		if(str == NULL)
			return std::string(length, ' ');

		std::string ret(str);
		if(ret.size() < length)
		{
			ret.append(length - ret.size(), ' ');
		}

		return std::move(ret);
	}

	/*!
	 * \brief 格式化字符串（va_list版本）
	 * \param pszFormat 格式化字符串
	 * \param argptr 参数列表
	 * \return 格式化后的字符串
	 * 
	 * \details 使用va_list的printf实现，主要版本
	 */
	static inline std::string printf(const char* pszFormat, va_list argptr)
	{
		char* buffer = NULL;
		int bufflen = 256;
		
		while(true)
		{
			buffer = new char[bufflen];
			va_list ap;
			va_copy(ap, argptr);
			int writelen = vsnprintf(buffer, bufflen, pszFormat, ap);
			va_end(ap);
			if(writelen < bufflen && writelen != -1)
			{
				std::string s(buffer);
				delete[] buffer;
				return s;
			}
			delete[] buffer;
			if(writelen == -1)
				bufflen*=2;
			else
				bufflen = writelen + 1;
		}
		return "";
	}

	/*!
	 * \brief 获取字符串右侧指定长度的子字符串
	 * \param src 源字符串
	 * \param nCount 要获取的字符数量
	 * \return 右侧子字符串
	 * 
	 * \details 返回字符串最右侧指定长度的子字符串
	 */
	static inline std::string right(const std::string &src,size_t nCount)
	{
		if(nCount >= src.length())
			return src;
		return src.substr(src.length() - nCount);
	}

	/*!
	 * \brief 获取字符串左侧指定长度的子字符串
	 * \param src 源字符串
	 * \param nCount 要获取的字符数量
	 * \return 左侧子字符串
	 * 
	 * \details 返回字符串最左侧指定长度的子字符串
	 */
	static inline std::string left(const std::string &src,size_t nCount)
	{
		return src.substr(0, nCount);
	}

	/*!
	 * \brief 统计字符在字符串中出现的次数
	 * \param src 源字符串
	 * \param ch 要统计的字符
	 * \return 字符出现的次数
	 * 
	 * \details 计算指定字符在字符串中出现的总次数
	 */
	static inline size_t charCount(const std::string &src,char ch)
	{
		size_t cnt = 0;
		for(auto &c : src)
		{
			if(c == ch)
				cnt++;
		}
		return cnt;
	}

	/*!
	 * \brief 字符串替换
	 * \param str 要处理的字符串（引用，会被修改）
	 * \param src 要被替换的字符串
	 * \param des 替换后的字符串
	 * 
	 * \details 将字符串中所有出现的src子字符串替换为des字符串
	 */
	static inline void replace(std::string& str, const char* src, const char* des)
	{
		std::string newsrc = src;
		std::string newdes = des;
		std::string::size_type pos = 0;
		std::string::size_type srclen = newsrc.size();
		std::string::size_type deslen = newdes.size();
		pos = str.find(newsrc, pos);
		while ((pos != std::string::npos))
		{
			str.replace(pos, srclen, newdes);
			pos = str.find(newsrc, (pos + deslen));
		}
	}
};
